#include "stdafx.h"
#include "texture.h"
#include "../common/common.h"
#include "MapManager.h"
#include "../gamemanager.h"
#include "Log.h"

DWORD Vertex_RHW_TEX::FVF = (D3DFVF_XYZRHW|D3DFVF_TEX1);
DWORD Vertex_RHW_TEX2::FVF = (D3DFVF_XYZRHW|D3DFVF_TEX2);
DWORD Vertex_RHW_CLR::FVF = (D3DFVF_XYZRHW|D3DFVF_DIFFUSE);
DWORD Vertex_RHW_CLR_TEX::FVF = (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1);

CD3DManager::CD3DManager()
{
    m_pFont = NULL;
    m_pDevice = NULL;
    m_pD3D = NULL;
    m_pSprite = NULL;
}

CD3DManager::~CD3DManager()
{
    CleanUp();
}

void CD3DManager::Initialize( HWND hwnd )
{
    m_pD3D=Direct3DCreate9(D3D_SDK_VERSION);
    D3DDISPLAYMODE d3dmm;
    m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3dmm);
    D3DPRESENT_PARAMETERS d3ddp;
    ZeroMemory(&d3ddp,sizeof(d3ddp));
    d3ddp.Windowed=true;
    d3ddp.SwapEffect=D3DSWAPEFFECT_DISCARD;
    d3ddp.BackBufferFormat=D3DFMT_A8R8G8B8;//d3dmm.Format;
    m_pD3D->CreateDevice(
        D3DADAPTER_DEFAULT,
        D3DDEVTYPE_HAL,
        hwnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING,
        &d3ddp,
        &m_pDevice);
    m_pDevice->SetRenderState(
        D3DRS_SHADEMODE,
        D3DSHADE_FLAT);
    m_hwnd = hwnd;
    //int displaymode = m_pDevice->GetDisplayMode( 0 );
    D3DXCreateSprite( m_pDevice, &m_pSprite );
    D3DXCreateFont( m_pDevice, 14, 0, 400, 1, FALSE, DEFAULT_CHARSET,
        OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
        L"Arial", &m_pFont );
}

int CD3DManager::AddTexture( LPCTSTR filename , int colorkey)
{
    for( size_t i = 0; i < m_textures.size(); i++ )
    {
        if( _tcscmp(m_textures[i].filename, filename) == 0 )
            return i;
    }
    IDirect3DTexture9 *tex = LoadTextureFromFile(filename, D3DFMT_A8R8G8B8, colorkey);
    if( tex )
    {
        TextureInfo info;
        _tcscpy( info.filename, filename );
        info.texture = tex;
        D3DSURFACE_DESC desc;
        tex->GetLevelDesc( 0, &desc );
        info.height = desc.Height;
        info.width = desc.Width;
        m_textures.push_back(info);
        return m_textures.size()-1;
    }
    g_log.WriteErr(TEXT("Add Texture:%s Failed"), filename);
    return -1;
}

IDirect3DTexture9 *CD3DManager::LoadTextureFromFile( LPCTSTR filename , int format , int colorkey)
{
    IDirect3DTexture9 *tex = NULL;
    HRESULT result = D3DXCreateTextureFromFileEx(m_pDevice, filename, D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT, D3DUSAGE_DYNAMIC, (D3DFORMAT)format, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,\
        colorkey, 0, 0, &tex);
    if( result != S_OK )
    {
        return NULL;
    }
    return tex;
}

void CD3DManager::Clear()
{
	m_pDevice->Clear(0,
		NULL,
		D3DCLEAR_TARGET,
		D3DCOLOR_XRGB(0,0,0),
		1.0f,
		0);
}

void CD3DManager::BeginRender()
{
    m_pDevice->BeginScene();
}

void CD3DManager::BeginRenderMap()
{
	//m_pDevice->LightEnable(0, false);
	m_pDevice->SetTextureStageState(0, D3DTSS_COLOROP,  D3DTOP_SELECTARG1 );
	m_pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
	m_pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
	m_pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
	m_pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE );
	m_pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

	//m_pDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	//m_pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	//m_pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
}

void CD3DManager::EndRenderMap()
{

}

void CD3DManager::BeginRenderText()
{
	m_pSprite->Begin( D3DXSPRITE_DONOTSAVESTATE );
}

void CD3DManager::EndRenderText()
{
	m_pSprite->End();
}

void CD3DManager::EndRender()
{
    m_pDevice->EndScene();
    m_pDevice->Present(NULL,NULL,NULL,NULL);
}

void CD3DManager::DrawTexture(int tex, int x, int y, int sizex, int sizey, float u1, float v1, float u2, float v2)
{
    if( tex >= 0 && tex < (int)m_textures.size() )
    {
		if( sizex == -1 )
			sizex = int(m_textures[tex].width * ( u2 - u1 ));
		if( sizey == -1 )
			sizey = int(m_textures[tex].height * ( v2 - v1 ));
        m_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
        float minx = (float)x;
        float miny = (float)y;
        float maxx = (float)(x+sizex-1);
        float maxy = (float)(y+sizey-1);
        Vertex_RHW_TEX ver[]=
        {
            {minx,miny,0.0f,1.0f,u1,v1},
            {maxx,miny,0.0f,1.0f,u2,v1},
            {maxx,maxy,0.0f,1.0f,u2,v2},

            {minx,miny,0.0f,1.0f,u1,v1},
            {maxx,maxy,0.0f,1.0f,u2,v2},
            {minx,maxy,0.0f,1.0f,u1,v2},
        };
        CD3DManager::Instance().GetDevice()->SetTexture( 0, m_textures[tex].texture );
        CD3DManager::Instance().GetDevice()->SetFVF(Vertex_RHW_TEX::FVF);
        CD3DManager::Instance().GetDevice()->DrawPrimitiveUP( D3DPT_TRIANGLELIST, 2, ver, sizeof(Vertex_RHW_TEX) );
    }


}

void CD3DManager::DrawRect( RECT &rect, DWORD color /*= 0xffff0000 */, bool fill /*= false */ )
{
    float minx = (float)rect.left;
    float miny = (float)rect.top;
    float maxx = (float)rect.right;
    float maxy = (float)rect.bottom;
    Vertex_RHW_CLR ver[]=
    {
        {minx,miny,0.0f,1.0f,color},
        {maxx,miny,0.0f,1.0f,color},
        {maxx,maxy,0.0f,1.0f,color},

        //{minx,miny,0.0f,1.0f,color},
        //{maxx,maxy,0.0f,1.0f,color},
        {minx,maxy,0.0f,1.0f,color},
        {minx,miny,0.0f,1.0f,color},
    };
    if(fill)
        m_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
    else
        m_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
    m_pDevice->SetFVF(Vertex_RHW_CLR::FVF);
    m_pDevice->SetTexture(0, 0);
    m_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 4, ver, sizeof(Vertex_RHW_CLR) );
}

void CD3DManager::CleanUp()
{
    SAFE_RELEASE( m_pD3D );
    //SAFE_DELETE(m_pD3D);
    SAFE_RELEASE( m_pDevice );
//    SAFE_DELETE(m_pDevice);
    SAFE_RELEASE( m_pFont );
    //SAFE_DELETE(m_pFont);
    SAFE_RELEASE( m_pSprite );
    //SAFE_DELETE(m_pSprite);
    for( size_t i = 0; i < m_textures.size(); i++ )
    {
        m_textures[i].texture->Release();
       // SAFE_DELETE(m_textures[i].texture);
    }
    m_textures.clear();
}

void CD3DManager::DrawText( LPCTSTR str, LPRECT rect, D3DCOLOR color /*= 0xff000000 */ )
{
    D3DXMATRIX matrix;
    D3DXMatrixIdentity( &matrix );
    m_pSprite->SetTransform( &matrix );
    int result = m_pFont->DrawText( m_pSprite, str, -1, rect, DT_CENTER|DT_VCENTER, color );
}

TextureInfo * CD3DManager::GetTextureInfo( int texid )
{
    if( texid >= 0 && texid < (int)m_textures.size() )
    {
        return &m_textures[texid];
    }
    return NULL;
}

CTexture::CTexture()
{
    m_pTexture = NULL;
}

CTexture::~CTexture()
{
    Cleanup();
}

bool CTexture::LoadTexture( LPCTSTR filename , int format/*=D3DFMT_A8R8G8B8 */)
{
    Cleanup();
    HRESULT result = D3DXCreateTextureFromFileEx(CD3DManager::Instance().GetDevice(), filename, D3DX_DEFAULT_NONPOW2 , D3DX_DEFAULT_NONPOW2 , D3DX_DEFAULT, D3DUSAGE_DYNAMIC, (D3DFORMAT)format, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,\
        0, 0, 0, &m_pTexture);
    if( result != S_OK )
    {
        return false;
    }
    D3DSURFACE_DESC desc;
    m_pTexture->GetLevelDesc(0, &desc);
    m_size.cx = desc.Width;
    m_size.cy = desc.Height;
    return true;
}

bool CTexture::LoadTexture( int width, int height,const char *srcdata , int format/*=D3DFMT_A8R8G8B8 */ )
{
    Cleanup();
    HRESULT result = D3DXCreateTexture( CD3DManager::Instance().GetDevice(), width, height, 0, D3DUSAGE_DYNAMIC, (D3DFORMAT)format, D3DPOOL_DEFAULT, &m_pTexture );
    if( result != S_OK )
    {
        return false;
    }
    D3DLOCKED_RECT lockedrect;
    result = m_pTexture->LockRect( 0, &lockedrect, 0, 0 );
    if( result != S_OK )
    {
        return false;
    }
    memcpy(lockedrect.pBits, srcdata, lockedrect.Pitch*height);
    m_pTexture->UnlockRect(0);

    D3DSURFACE_DESC desc;
    m_pTexture->GetLevelDesc(0, &desc);
    m_size.cx = desc.Width;
    m_size.cy = desc.Height;

    //static int a=0;
    //a++;
    //TCHAR filename[32] = {0};
    //_stprintf( filename, L"data\\%d\.tga", a );
    //D3DXSaveTextureToFile( filename, D3DXIFF_TGA, m_pTexture, 0 );

    return true;
}

bool CTexture::LoadTexture( int srcwidth, int srcheight,RECT &rect,const char *srcdata , int format/*=D3DFMT_A8R8G8B8 */ )
{
    Cleanup();
    
    m_size.cx = rect.right-rect.left+1;
    m_size.cy = rect.bottom-rect.top+1;
    HRESULT result = D3DXCreateTexture( CD3DManager::Instance().GetDevice(), m_size.cx, m_size.cy, 0, D3DUSAGE_DYNAMIC, (D3DFORMAT)format, D3DPOOL_DEFAULT, &m_pTexture );
    if( result != S_OK )
    {
        return false;
    }
    D3DSURFACE_DESC desc;
    m_pTexture->GetLevelDesc(0, &desc);
    int width = desc.Width;
    int height = desc.Height;

    D3DLOCKED_RECT lockedrect;
    result = m_pTexture->LockRect( 0, &lockedrect, 0, 0 );
    if( result != S_OK )
    {
        return false;
    }
    char *pDest = (char*)lockedrect.pBits;
    for(int i=rect.top; i<=rect.bottom; i++)
    {
        memcpy(&pDest[(i-rect.top)*lockedrect.Pitch], &srcdata[sizeof(DWORD)*(i*srcwidth+rect.left)], width*sizeof(DWORD));
    }
    m_pTexture->UnlockRect(0);
    m_size.cx = desc.Width;
    m_size.cy = desc.Height;

    //static int a=0;
    //a++;
    //TCHAR filename[32] = {0};
    //_stprintf( filename, L"data\\%d\.tga", a );
    //D3DXSaveTextureToFile( filename, D3DXIFF_TGA, m_pTexture, 0 );

    return true;
}

void CTexture::Render( int x, int y, int sizex, int sizey, int color, float u1 /*= 0.0f*/, float v1 /*= 0.0f*/, float u2 /*= 1.0f*/, float v2 /*= 1.0f*/ )
{
    if( !m_pTexture)
        return;
    float minx = (float)x;
    float miny = (float)y;
    float maxx = (float)(x+sizex-1);
    float maxy = (float)(y+sizey-1);
    Vertex_RHW_CLR_TEX ver[]=
    {
        {minx,miny,0.0f,1.0f,color,u1,v1},
        {maxx,miny,0.0f,1.0f,color,u2,v1},
        {maxx,maxy,0.0f,1.0f,color,u2,v2},

        {minx,miny,0.0f,1.0f,color,u1,v1},
        {maxx,maxy,0.0f,1.0f,color,u2,v2},
        {minx,maxy,0.0f,1.0f,color,u1,v2},
    };
    CD3DManager::Instance().GetDevice()->SetTexture( 0, m_pTexture );
    CD3DManager::Instance().GetDevice()->SetFVF(Vertex_RHW_CLR_TEX::FVF);
    CD3DManager::Instance().GetDevice()->DrawPrimitiveUP( D3DPT_TRIANGLELIST, 2, ver, sizeof(Vertex_RHW_CLR_TEX) );
}

void CTexture::Cleanup()
{
    SAFE_RELEASE(m_pTexture);
}

void CTexture::SaveData( std::ofstream &f )
{
    if(!m_pTexture)
        return;
    D3DLOCKED_RECT lockedrect;
    D3DSURFACE_DESC desc;
    m_pTexture->GetLevelDesc(0, &desc);
    HRESULT result = m_pTexture->LockRect( 0, &lockedrect, 0, 0 );
    int *pData = (int*)lockedrect.pBits;
    f.write((const char*)&desc, sizeof(desc));
	f.write((const char*)&lockedrect.Pitch, sizeof(lockedrect.Pitch));
    f.write((const char*)lockedrect.pBits, desc.Height*lockedrect.Pitch);

    m_pTexture->UnlockRect(0);

    //static int a=0;
    //a++;
    //TCHAR filename[32] = {0};
    //_stprintf( filename, L"data\\later%d\.tga", a );
    //result = D3DXSaveTextureToFile( filename, D3DXIFF_TGA, m_pTexture, 0 );
}

void CTexture::ComputeScanline( int startx, int starty, int id )
{
    D3DLOCKED_RECT lockedrect;
    D3DSURFACE_DESC desc;
    m_pTexture->GetLevelDesc(0, &desc);
    HRESULT result = m_pTexture->LockRect( 0, &lockedrect, 0, 0 );
    DWORD *data = (DWORD*)lockedrect.pBits;
    int width = lockedrect.Pitch/sizeof(DWORD);
    bool isstart = false;
    int start = 0;
    int end = 0;
    for( uint32 i=0; i<desc.Height; i++ )
    {
        isstart = false;
        for( uint32 j=0;j<desc.Width;j++ )
        {
            DWORD alpha = (data[i*width+j])>>24;
            if( alpha==0 && isstart == true )
			{
				end = j;
				if( end - start >= 4 )  
					CMapManager::Instance().AddTileLine(i+starty, start+startx, id, end+startx);
				isstart = false;
			}
			else if( alpha == 0xff && isstart == false )
			{
				isstart = true;
				start = j;
			}
        }
		if(isstart == true)
		{
			end = desc.Width;
			if( end - start >= 4 )  
				CMapManager::Instance().AddTileLine(i+starty, start+startx, id, end+startx);
		}
    }
    m_pTexture->UnlockRect(0);
}

void CTexture::SetColor( int color )
{
    if(!m_pTexture)
        return;
    D3DLOCKED_RECT lockedrect;
    D3DSURFACE_DESC desc;
    m_pTexture->GetLevelDesc(0, &desc);
    HRESULT result = m_pTexture->LockRect( 0, &lockedrect, 0, 0 );
    DWORD *data = (DWORD*)lockedrect.pBits;
    int width = lockedrect.Pitch/sizeof(DWORD);
    for( uint32 i=0;i<desc.Height;i++ )
    {
        for( uint32 j=0;j<lockedrect.Pitch/sizeof(DWORD);j++ )
        {
            int index = i*width+j;
            DWORD oldcolor = data[index];
            if( (oldcolor>>24)!= 0 )
            {
                data[index] = color;
            }
        }
    }
    m_pTexture->UnlockRect(0);
}

SIZE & CTexture::GetSize()
{
    return m_size;
}

bool CTexture::SaveTextureToFile( const TCHAR *filename , int format)
{
    HRESULT r = D3DXSaveTextureToFile( filename, (D3DXIMAGE_FILEFORMAT)format, m_pTexture, 0 );
    if( r != S_OK )
    {
        g_log.WriteErr( L"Write texture to file %s failed, error %d.", filename, r );
        return false;
    }
    return true;
}
